这里有两种方式可以为应用添加Servlet
,Filter
,ServletContextListener
和其他Servlet支持的特定listeners。你既可以为它们提供Spring beans,也可以为Servlet组件启用扫描(package scan)。
想要添加Servlet
,Filter
或Servlet*Listener
,你只需要为它提供一个@Bean
定义,这种方式很适合注入配置或依赖。不过,需要注意的是它们不会导致其他很多beans的热初始化,因为它们需要在应用生命周期的早期进行安装(让它依赖DataSource
或JPA配置不是好主意),你可以通过懒加载突破该限制(在第一次使用时才初始化)。
对于Filters
或Servlets
,你可以通过FilterRegistrationBean
或ServletRegistrationBean
添加映射和初始化参数。
注 在一个filter注册时,如果没指定dispatcherType
,它将匹配FORWARD
,INCLUDE
和REQUEST
。如果启用异步,它也将匹配ASYNC
。如果迁移web.xml
中没有dispatcher
元素的filter,你需要自己指定一个dispatcherType
:
@Bean
public FilterRegistrationBean myFilterRegistration() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setDispatcherTypes(DispatcherType.REQUEST);
....
return registration;
}
禁止Servlet或Filter的注册
如上所述,任何Servlet
或Filter
beans都将自动注册到servlet容器。不过,为特定的Filter
或Servlet
bean创建一个registration,并将它标记为disabled,可以禁用该filter或servlet。例如:
@Bean
public FilterRegistrationBean registration(MyFilter filter) {
FilterRegistrationBean registration = new FilterRegistrationBean(filter);
registration.setEnabled(false);
return registration;
}
通过把@ServletComponentScan
注解到一个@Configuration
类并指定包含要注册组件的package(s),可以将@WebServlet
,@WebFilter
和@WebListener
注解的类自动注册到内嵌servlet容器。默认情况下,@ServletComponentScan
将从被注解类的package开始扫描。
在一个单独的应用中,主HTTP端口默认为8080
,不过可以使用server.port
设置(比如,在application.properties
中或作为系统属性)。由于Environment
值的宽松绑定,你也可以使用SERVER_PORT
(比如,作为OS环境变量)。
想要创建WebApplicationContext
但完全关闭HTTP端点,你可以设置server.port=-1
(测试时可能有用)。具体详情可查看'Spring Boot特性'章节的Section 27.3.4, “Customizing embedded servlet containers”,或ServerProperties源码。
想扫描获取一个未使用的端口(使用操作系统本地端口以防冲突)可以设置server.port=0
。
你可以通过日志输出或它的EmbeddedServletContainer
的EmbeddedWebApplicationContext
获取服务器正在运行的端口。获取和确认服务器已经初始化的最好方式是添加一个ApplicationListener<EmbeddedServletContainerInitializedEvent>
类型的@Bean
,然后当事件发布时将容器pull出来。
使用@SpringBootTest(webEnvironment=WebEnvironment.RANDOM_PORT)
进行测试时,你可以通过@LocalServerPort
注解将实际端口注入到字段中,例如:
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(webEnvironment=WebEnvironment.RANDOM_PORT)
public class MyWebIntegrationTests {
@Autowired
EmbeddedWebApplicationContext server;
@LocalServerPort
int port;
// ...
}
注 @LocalServerPort
是@Value("${local.server.port}")
的元数据,在常规的应用中不要尝试注入端口。正如我们看到的,该值只会在容器初始化后设置。相对于测试,应用代码回调处理的会更早(例如在该值实际可用之前)。
你可以以声明方式配置SSL,一般通过在application.properties
或application.yml
设置各种各样的server.ssl.*
属性,例如:
server.port = 8443
server.ssl.key-store = classpath:keystore.jks
server.ssl.key-store-password = secret
server.ssl.key-password = another-secret
查看Ssl获取所有支持的配置。
使用类似于以上示例的配置意味着该应用将不支持端口为8080的普通HTTP连接。Spring Boot不支持通过application.properties
同时配置HTTP连接器和HTTPS连接器。如果你两个都想要,那就需要以编程的方式配置它们中的一个。推荐使用application.properties
配置HTTPS,因为HTTP连接器是两个中最容易以编程方式进行配置的,查看spring-boot-sample-tomcat-multi-connectors可获取示例项目。
通过相应的命令空间可以为Tomcat和Undertow配置访问日志,例如下面是为Tomcat配置的一个自定义模式的访问日志:
server.tomcat.basedir=my-tomcat
server.tomcat.accesslog.enabled=true
server.tomcat.accesslog.pattern=%t %a "%r" %s (%D ms)
注 日志默认路径为tomcat基础路径下的logs
目录,该dir默认是个临时目录,所以你可能想改变Tomcat的base目录或为日志指定绝对路径。上述示例中,你可以在相对于应用工作目录的my-tomcat/logs
访问到日志。
Undertow的访问日志配置方式类似:
server.undertow.accesslog.enabled=true
server.undertow.accesslog.pattern=%t %a "%r" %s (%D ms)
日志存储在相对于应用工作目录的logs
目录下,可以通过server.undertow.accesslog.directory
自定义。
你的应用可能需要发送302
跳转或使用指向自己的绝对路径渲染内容。当在代理服务器后面运行时,调用者需要的是代理服务器链接而不是部署应用的实际物理机器地址,通常的解决方式是代理服务器将前端地址放到headers并告诉后端服务器如何拼装链接。
如果代理添加约定的X-Forwarded-For
和X-Forwarded-Proto
headers(大多数都是开箱即用的),只要将application.properties
中的server.use-forward-headers
设置为true
,绝对链接就能正确的渲染。
注 如果应用运行在Cloud Foundry或Heroku,server.use-forward-headers
属性没指定的话默认为true
,其他实例默认为false
。
如果使用的是Tomcat,你可以配置用于传输"forwarded"信息的headers名:
server.tomcat.remote-ip-header=x-your-remote-ip-header
server.tomcat.protocol-header=x-your-protocol-header
你也可以为Tomcat配置一个默认的正则表达式,用来匹配内部信任的代理。默认情况下,IP地址10/8
,192.168/16
,169.254/16
和127/8
是被信任的。通过设置server.tomcat.internal-proxies
属性可以自定义,比如:
server.tomcat.internal-proxies=192\\.168\\.\\d{1,3}\\.\\d{1,3}
注 只有在使用配置文件时才需要双反斜线,如果使用YAML,只需要单个反斜线,比如192\.168\.\d{1,3}\.\d{1,3}
。
注 将internal-proxies
设置为空表示信任所有代理,不要在生产环境使用。
你可以完全控制Tomcat的RemoteIpValve
配置,只要关掉自动配置(比如设置server.use-forward-headers=false
)并在TomcatEmbeddedServletContainerFactory
bean添加一个新value实例。
通常你可以遵循Section 69.8, “Discover built-in options for external properties”关于@ConfigurationProperties
(这里主要的是ServerProperties
)的建议,但也看下EmbeddedServletContainerCustomizer
和各种你可以添加的Tomcat-specific的*Customizers
。
Tomcat APIs相当丰富,一旦获取到TomcatEmbeddedServletContainerFactory
,你就能够以多种方式修改它,或更彻底地就是添加你自己的TomcatEmbeddedServletContainerFactory
。
你可以将org.apache.catalina.connector.Connector
添加到TomcatEmbeddedServletContainerFactory
,这就能够允许多连接器,比如HTTP和HTTPS连接器:
@Bean
public EmbeddedServletContainerFactory servletContainer() {
TomcatEmbeddedServletContainerFactory tomcat = new TomcatEmbeddedServletContainerFactory();
tomcat.addAdditionalTomcatConnectors(createSslConnector());
return tomcat;
}
private Connector createSslConnector() {
Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
Http11NioProtocol protocol = (Http11NioProtocol) connector.getProtocolHandler();
try {
File keystore = new ClassPathResource("keystore").getFile();
File truststore = new ClassPathResource("keystore").getFile();
connector.setScheme("https");
connector.setSecure(true);
connector.setPort(8443);
protocol.setSSLEnabled(true);
protocol.setKeystoreFile(keystore.getAbsolutePath());
protocol.setKeystorePass("changeit");
protocol.setTruststoreFile(truststore.getAbsolutePath());
protocol.setTruststorePass("changeit");
protocol.setKeyAlias("apitester");
return connector;
}
catch (IOException ex) {
throw new IllegalStateException("can't access keystore: [" + "keystore"
+ "] or truststore: [" + "keystore" + "]", ex);
}
}
Spring Boot使用的内嵌Tomcat不能开箱即用的支持Version 0
的Cookie格式,你可能会看到以下错误:
java.lang.IllegalArgumentException: An invalid character [32] was present in the Cookie value
可以的话,你需要考虑将代码升级到只存储遵从最新版Cookie定义的值。如果不能改变写入的cookie,你可以配置Tomcat使用LegacyCookieProcessor
。通过向EmbeddedServletContainerCustomizer
bean添加一个TomcatContextCustomizer
可以开启LegacyCookieProcessor
:
@Bean
public EmbeddedServletContainerCustomizer cookieProcessorCustomizer() {
return new EmbeddedServletContainerCustomizer() {
@Override
public void customize(ConfigurableEmbeddedServletContainer container) {
if (container instanceof TomcatEmbeddedServletContainerFactory) {
((TomcatEmbeddedServletContainerFactory) container)
.addContextCustomizers(new TomcatContextCustomizer() {
@Override
public void customize(Context context) {
context.setCookieProcessor(new LegacyCookieProcessor());
}
});
}
}
};
}
Spring Boot starters(特别是spring-boot-starter-web
)默认都使用Tomcat作为内嵌容器。想使用Jetty替代Tomcat,你需要排除那些Tomcat的依赖并包含Jetty的依赖。为了简化这种事情的处理,Spring Boot将Tomcat和Jetty的依赖捆绑在一起,然后提供了单独的starters。
Maven示例:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
Gradle示例:
configurations {
compile.exclude module: "spring-boot-starter-tomcat"
}
dependencies {
compile("org.springframework.boot:spring-boot-starter-web:1.4.1.RELEASE")
compile("org.springframework.boot:spring-boot-starter-jetty:1.4.1.RELEASE")
// ...
}
通常你可以遵循Section 69.8, “Discover built-in options for external properties”关于@ConfigurationProperties
(此处主要是ServerProperties
)的建议,但也要看下EmbeddedServletContainerCustomizer
。
Jetty API相当丰富,一旦获取到JettyEmbeddedServletContainerFactory
,你就可以使用很多方式修改它,或更彻底地就是添加你自己的JettyEmbeddedServletContainerFactory
。
使用Undertow替代Tomcat和使用Jetty替代Tomcat非常类似。你需要排除Tomat依赖,并包含Undertow starter。
Maven示例:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
Gradle示例:
configurations {
compile.exclude module: "spring-boot-starter-tomcat"
}
dependencies {
compile 'org.springframework.boot:spring-boot-starter-web:1.3.0.BUILD-SNAPSHOT")
compile 'org.springframework.boot:spring-boot-starter-undertow:1.3.0.BUILD-SNAPSHOT")
// ...
}
通常你可以遵循Section 69.8, “Discover built-in options for external properties”关于@ConfigurationProperties
(此处主要是ServerProperties
和ServerProperties.Undertow
),但也要看下EmbeddedServletContainerCustomizer
。
一旦获取到UndertowEmbeddedServletContainerFactory
,你就可以使用UndertowBuilderCustomizer
修改Undertow的配置以满足你的需求,或更彻底地就是添加你自己的UndertowEmbeddedServletContainerFactory
。
将UndertowBuilderCustomizer
添加到UndertowEmbeddedServletContainerFactory
,然后使用Builder
添加一个listener:
@Bean
public UndertowEmbeddedServletContainerFactory embeddedServletContainerFactory() {
UndertowEmbeddedServletContainerFactory factory = new UndertowEmbeddedServletContainerFactory();
factory.addBuilderCustomizers(new UndertowBuilderCustomizer() {
@Override
public void customize(Builder builder) {
builder.addHttpListener(8080, "0.0.0.0");
}
});
return factory;
}
Spring Boot可以使用Tomcat7&8.0,但默认使用的是Tomcat8.5。如果不能使用Tomcat8.5(例如,因为你使用的是Java1.6),你需要改变classpath去引用一个不同版本。
如果正在使用starters 和parent,你只需要改变Tomcat的version
属性,并添加tomcat-juli
依赖。比如,对于一个简单的webapp或service:
<properties>
<tomcat.version>7.0.59</tomcat.version>
</properties>
<dependencies>
...
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-juli</artifactId>
<version>${tomcat.version}</version>
</dependency>
...
</dependencies>
对于Gradle,你可以通过设置tomcat.version
属性改变Tomcat的版本,然后添加tomcat-juli
依赖:
ext['tomcat.version'] = '7.0.59'
dependencies {
compile 'org.springframework.boot:spring-boot-starter-web'
compile group:'org.apache.tomcat', name:'tomcat-juli', version:property('tomcat.version')
}
Spring Boot可以使用Jetty9.2,但默认使用的是Jetty9.3。如果不能使用Jetty9.3(例如,因为你使用的是Java7),你需要改变classpath去引用Jetty9.2。
如果正在使用starters和parent,你只需添加Jetty starter并覆盖jetty.version
属性:
<properties>
<jetty.version>9.2.17.v20160517</jetty.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
</dependencies>
对于Gradle,你需要设置jetty.version
属性,例如对于一个简单的webapp或service:
ext['jetty.version'] = '9.2.17.v20160517'
dependencies {
compile ('org.springframework.boot:spring-boot-starter-web') {
exclude group: 'org.springframework.boot', module: 'spring-boot-starter-tomcat'
}
compile ('org.springframework.boot:spring-boot-starter-jetty')
}
Spring Boot支持Jetty 8,但默认使用的是Jetty 9.3。如果不能使用Jetty 9.3(比如因为你使用的是Java 1.6),你需要改变classpath去引用Jetty 8,还需要排除Jetty的WebSocket相关依赖。
如果正在使用starters和parent,你只需要添加Jetty starter,排除那些需要的WebSocket,并改变version属性。比如,对于一个简单的webapp或service:
<properties>
<jetty.version>8.1.15.v20140411</jetty.version>
<jetty-jsp.version>2.2.0.v201112011158</jetty-jsp.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
<exclusions>
<exclusion>
<groupId>org.eclipse.jetty.websocket</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
你可以设置jetty.version
属性并排除相关的WebSocket依赖,比如对于一个简单的webapp或service:
ext['jetty.version'] = '8.1.15.v20140411'
dependencies {
compile ('org.springframework.boot:spring-boot-starter-web') {
exclude group: 'org.springframework.boot', module: 'spring-boot-starter-tomcat'
}
compile ('org.springframework.boot:spring-boot-starter-jetty') {
exclude group: 'org.eclipse.jetty.websocket'
}
}
如果想在使用内嵌容器的Spring Boot应用中使用@ServerEndpoint
,你需要声明一个单独的ServerEndpointExporter
@Bean
:
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
该bean将使用底层的WebSocket容器注册任何被@ServerEndpoint
注解的beans。当部署到一个单独的servlet容器时,该角色将被一个servlet容器初始化方法执行,ServerEndpointExporter
bean也就不需要了。
Jetty,Tomcat和Undertow支持HTTP响应压缩,你可以通过设置server.compression.enabled
启用它:
server.compression.enabled=true
默认情况下,响应信息长度至少2048字节才能触发压缩,通过server.compression.min-response-size
属性可以改变该长度。另外,只有响应的content type为以下其中之一时才压缩:
text/html
text/xml
text/plain
text/css
你可以通过server.compression.mime-types
属性配置。